
#define DIRECTINPUT_VERSION 0x0800

#include <windows.h>
#include <dinput.h>
#include <d3d9.h>
#include <d3dx9.h>
#include <stdio.h>
#include <cegfx/ce3rdpersoncamera.h>
#include <cegfx/cecamera.h>
#include <cegfx/ceeffect.h>
#include <cegfx/cegeometrynode.h>
#include <cegfx/ceinstancedmesh.h>
#include <cegfx/cekeyboard.h>
#include <cegfx/celodnodecollection.h>
#include <cegfx/cematerial.h>
#include <cegfx/cematerialmanager.h>
#include <cegfx/cemouse.h>
#include <cegfx/cemorphmesh.h>
#include <cegfx/cenodecollection.h>
#include <cegfx/ceparticelmesh.h>
#include <cegfx/ceparticelnode.h>
#include <cegfx/cephysics.h>
#include <cegfx/cepostprocessrenderer.h>
#include <cegfx/cerendercontroller.h>
#include <cegfx/cerendermesh.h>
#include <cegfx/cescene.h>
#include <cegfx/cesector.h>
#include <cegfx/ceterrainmesh.h>
#include <cegfx/cetimer.h>
#include <cegfx/cevector3.h>
#include <cegfx/cevirtualclock.h>
#include <cegfx/igeometryanimator.h>
#include <cegfx/ilodsolver.h>
#include <cegfx/inodeanimator.h>
#include <cegfx/iviewport.h>
#include <cegfx/ceskeleton.h>
#include <btBulletCollisionCommon.h>
#include <cegame/ced3d9textureloader.h>
#include <cegame/ceeffectloader.h>
#include <cegame/cexmlmaterialloader.h>

class MorphAnimator : public iGeometryAnimator
{
	IQF_DECLARATION;
private:
	float value;

public:
	MorphAnimator ()
		: value (0.0f)
	{
		IQF_CONSTRUCT;
	}

	void UpdateEffectStates (ceEffect* effect)
	{
		effect->GetEffect ()->SetFloat ("stage", value);
	}

	void UpdateGeometry (iGeometry* geometry)
	{
		iMorphMesh* mm = IQFQueryInterface<iMorphMesh>(geometry);
		if (mm)
		{
			mm->SetMorphFrame(1, 0, value);
		}
	}

	void Advance ()
	{
		value += 0.01f;
		if (value >= 1.0f)
		{
			value -= 1.0f;
		}
	}
};

class BlockNodeAnimator : public iNodeAnimator
{
	IQF_DECLARATION;
private:
	D3DXVECTOR3 from;
	D3DXVECTOR3	to;

	float start;
	float end;

public:
	BlockNodeAnimator (const D3DXVECTOR3& from, const D3DXVECTOR3& to, float startTime, float time)
	{
		IQF_CONSTRUCT;
		Reset (from, to, startTime, time);
	}

	void Reset (const D3DXVECTOR3& from, const D3DXVECTOR3& to, float startTime, float time)
	{
		this->from = from;
		this->to = to;
		this->start = ceVClock::GetMilliseconds () + startTime;
		this->end = this->start + time;
	}


	void UpdateNode (const ceUpdateEnv& env, ceNode* node)
	{
		node->BeginTransformation ();
		if (env.CurrentTime < start )
		{
			node->GetTransform().SetTranslation (from);
		}
		else if (env.CurrentTime > end)
		{
			node->GetTransform ().SetTranslation (to);
		}
		else
		{
			float v = (env.CurrentTime - start) / (end - start);
			D3DXVECTOR3 p = from + v * (to - from);
			node->GetTransform ().SetTranslation (p);
		}
		node->EndTransformation ();
	}


};



class LODSolver : public iLODSolver
{
	IQF_DECLARATION;
private:
	float maxSize;

public:
	LODSolver (float maxSize) 
		: maxSize (maxSize*maxSize)
	{
		IQF_CONSTRUCT;
	}

	unsigned Solve (float sqrDistance)
	{
		return sqrDistance / maxSize;
	}
};

class MyViewport : public iViewport
{
private:
	IQF_DECLARATION;

	unsigned width;
	unsigned height;
public:
	MyViewport (unsigned width, unsigned height)
		: width (width)
		, height (height)
	{
		IQF_CONSTRUCT;
	}

	unsigned GetWidth () const
	{
		return width;
	}

	unsigned GetHeight () const
	{
		return height;
	}

	int GetX () const
	{
		return 0;
	}

	int GetY () const
	{
		return 0;
	}
};

IQF_IMPLEMENTATION_BEGIN(MorphAnimator);
IQF_IMPLEMENTATION_INTERFACE(iGeometryAnimator);
IQF_IMPLEMENTATION_END();

IQF_IMPLEMENTATION_BEGIN(BlockNodeAnimator);
IQF_IMPLEMENTATION_INTERFACE(iNodeAnimator);
IQF_IMPLEMENTATION_END();

IQF_IMPLEMENTATION_BEGIN(LODSolver);
IQF_IMPLEMENTATION_INTERFACE(iLODSolver);
IQF_IMPLEMENTATION_END();

IQF_IMPLEMENTATION_BEGIN(MyViewport);
IQF_IMPLEMENTATION_INTERFACE(iViewport);
IQF_IMPLEMENTATION_END();

#define WIDTH	1024
#define HEIGHT 768

#define main1 main

bool debug_node_collection;

LPDIRECT3D9	d3d;
LPDIRECT3DDEVICE9	d3dDevice;
LPDIRECTINPUT8 lpInput = 0;
LPDIRECT3DTEXTURE9 pp0Texture;
LPDIRECT3DTEXTURE9 pp0Effect;

ceMouse* mouse = 0;
ceKeyboard* keyboard = 0;
ce3rdPersonCamera* camera = 0;
ceScene* scene;
ceRenderController* controller;
ceMaterialManager* matManager;
MyViewport* viewport;
ceEffect* ppTest0;

ceSkeleton* skeleton;


cePhysics* physics;
ceParticelNode* dustNode = 0;

bool running = true;

D3DXMATRIX matWorld, matView, matProj;
D3DLIGHT9 light;

LPDIRECT3DVERTEXBUFFER9 lpVB0;
LPDIRECT3DVERTEXBUFFER9 lpVB1;
LPDIRECT3DINDEXBUFFER9 lpIB;
LPDIRECT3DVERTEXDECLARATION9 lpVD;

std::vector<BlockNodeAnimator*> blockAnimators;
ceParticelNode* blockParticels;
ceParticelNode* blockParticels2;
bool blockState = false;

void HandleBlock (bool block)
{
	if (block == blockState)
	{
		// no change
		return;
	}

	blockParticels->SetEmitting (block);
	blockParticels2->SetEmitting(block);
	if (block)
	{
		for (unsigned i=0; i<blockAnimators.size (); i++)
		{
			float t = 60.0f - i * 22.0f;
			float b = -40.0f - i * 30.0f;
			float time = 300.0f + i * 20.0f;
			blockAnimators[i]->Reset (D3DXVECTOR3(200.0f, b, 200.0f), D3DXVECTOR3(200.0f, t, 200.0f), 0.0f, time);
		}
	}
	else
	{
		for (unsigned i=0; i<blockAnimators.size (); i++)
		{
			float t = 60.0f - i * 22.0f;
			float b = -40.0f - i * 30.0f;
			float time = 300.0f - i * 20.0f;
			blockAnimators[i]->Reset (D3DXVECTOR3(200.0f, t, 200.0f), D3DXVECTOR3(200.0f, b, 200.0f), 0.0f, time);
		}
	}
	blockState = block;
}


#define RANDOM(x) rand() * (x) / RAND_MAX

struct Vertex
{
	float x, y, z;
	float nx, ny, nz;
	float tu, tv;

	Vertex () { }
	Vertex (float x, float y, float z, float nx, float ny, float nz, float tu, float tv) : x(x), y(y), z(z), nx(nx), ny(ny), nz(nz), tu(tu), tv(tv) { }
};

#define VERTEX_FVF (D3DFVF_XYZ | D3DFVF_DIFFUSE)


ceRenderMesh* CreateBoneMesh (unsigned numRads, float radius, float boneLength, unsigned numBones)
{
	struct Vertex
	{
		float x, y, z;
		float nx, ny, nz;
		float tu, tv;
		short id0, id1;
	};

	unsigned numVerts = numRads * 2 * numBones;
	unsigned numIndices = numRads * (numBones * 2 - 1) * 6;

	Vertex* vertices = new Vertex [numVerts];
	unsigned short* indices = new unsigned short[numIndices];

	Vertex* vptr = vertices;
	unsigned short* iptr = indices;
	for (unsigned i=0; i<numBones * 2; i++)
	{
		float tu = (float)i / (float)(numBones * 2 - 1);
		for (unsigned j=0; j<numRads; j++, vptr++)
		{
			float a = (float)j / (float)numRads; 
			float angle = a * 2.0f * D3DX_PI;

			vptr->x = cos (angle) * radius;
			vptr->y = sin (angle) * radius;
			vptr->z = (i % 2) * boneLength / 2.0f;

			vptr->nx = cos(angle);
			vptr->ny = sin(angle);
			vptr->nz = 0.0f;

			vptr->tu = tu;
			vptr->tv = a;
			vptr->id0 = i / 2;
			vptr->id1 = i / 2;

			if (i != numBones * 2 - 1)
			{
				unsigned i0 = i * numRads;
				unsigned i00 = i0 + j;
				unsigned i10 = j == numRads-1 ? i0 : i00 + 1;
				unsigned i01 = i00 + numRads;
				unsigned i11 = i10 + numRads;

				*iptr++ = i00;
				*iptr++ = i10;
				*iptr++ = i11;

				*iptr++ = i00;
				*iptr++ = i11;
				*iptr++ = i01;
			}
		}
	}

	LPDIRECT3DVERTEXDECLARATION9 d3dvd;
	LPDIRECT3DVERTEXBUFFER9 d3dvb;
	LPDIRECT3DINDEXBUFFER9 d3dib;
	void* data;

	D3DVERTEXELEMENT9 elements [] = {
		{ 0,   0, D3DDECLTYPE_FLOAT3,		D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION,			0 },
		{ 0,  12, D3DDECLTYPE_FLOAT3,	  D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL,				0 },
		{ 0,  24, D3DDECLTYPE_FLOAT2,	  D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD,			0 },
		{ 0,  32, D3DDECLTYPE_SHORT2,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDINDICES, 0},
		D3DDECL_END ()
	};

	if (FAILED(d3dDevice->CreateVertexDeclaration (elements, &d3dvd))) return false;
	if (FAILED(d3dDevice->CreateVertexBuffer (sizeof (Vertex) * numVerts, 0, VERTEX_FVF, D3DPOOL_DEFAULT, &d3dvb, 0))) return false;
	if (FAILED(d3dDevice->CreateIndexBuffer (sizeof (unsigned short) * numIndices, 0, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &d3dib, 0))) return false;

	if (SUCCEEDED(d3dvb->Lock (0, sizeof (Vertex) * numVerts, &data, 0)))
	{
		memcpy (data, vertices, sizeof (Vertex) * numVerts);
		d3dvb->Unlock();
	}
	else
	{
		return 0;
	}
	if (SUCCEEDED(d3dib->Lock (0, sizeof (unsigned short) * numIndices, &data, 0)))
	{
		memcpy (data, indices, sizeof (unsigned short) * numIndices);
		d3dib->Unlock ();
	}
	else
	{
		return 0;
	}


	ceRenderMesh* rmesh = new ceRenderMesh ();
	rmesh->SetVertices(d3dvb, numVerts);
	rmesh->SetIndices (d3dib, numIndices / 3);
	rmesh->SetVertexDeclaration (d3dvd, sizeof (Vertex));
	rmesh->GetBoundingBox ().Clear ();
	for (unsigned i=0; i<numVerts; i++)
	{
		rmesh->GetBoundingBox ().AddVector3 (vertices[i].x, vertices[i].y, vertices[i].z);
	}
	rmesh->GetBoundingBox ().Update ();

	delete [] vertices;
	delete [] indices;

  return rmesh;
}

ceInstancedMesh* CreateGrass (float radius, float height)
{
	unsigned numRads = 3;
	unsigned numVertices = 4 * numRads;
	unsigned numIndices = 6 * numRads;
	Vertex* vertices = new Vertex[numVertices];
	unsigned short* indices = new unsigned short [numIndices];
	for (unsigned i=0; i<numRads; i++)
	{
		float a = (float)i * D3DX_PI / (float)numRads;

		float x0 = - cos (a) * radius;
		float y0 = 0.0f;
		float z0 = sin (a) * radius;

		float x1 = cos (a) * radius;
		float y1 = height;
		float z1 = -sin (a) * radius;

		vertices [i * 4 + 0] = Vertex (x0, y0, z0, 0, 1, 0, 0, 1);
		vertices [i * 4 + 1] = Vertex (x0, y1, z0, 0, 1, 0, 0, 0);
		vertices [i * 4 + 2] = Vertex (x1, y1, z1, 0, 1, 0, 1, 0);
		vertices [i * 4 + 3] = Vertex (x1, y0, z1, 0, 1, 0, 1, 1);

		unsigned i0 = i * 4;

		indices [i * 6 + 0] = i0 + 0;
		indices [i * 6 + 1] = i0 + 1;
		indices [i * 6 + 2] = i0 + 2;
		indices [i * 6 + 3] = i0 + 0;
		indices [i * 6 + 4] = i0 + 2;
		indices [i * 6 + 5] = i0 + 3;

		/*
		indices [i * 12 + 6] = i0 + 0;
		indices [i * 12 + 7] = i0 + 2;
		indices [i * 12 + 8] = i0 + 1;
		indices [i * 12 + 9] = i0 + 0;
		indices [i * 12 + 10] = i0 + 3;
		indices [i * 12 + 11] = i0 + 2;
		*/
	}
	LPDIRECT3DVERTEXDECLARATION9 d3dvd;
	LPDIRECT3DVERTEXBUFFER9 d3dvb;
	LPDIRECT3DINDEXBUFFER9 d3dib;
	void* data;

	D3DVERTEXELEMENT9 elements [] = {
		{ 0,   0, D3DDECLTYPE_FLOAT3,		D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION,		0 },
		{ 0,  12, D3DDECLTYPE_FLOAT3,	  D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL,			0 },
		{ 0,  24, D3DDECLTYPE_FLOAT2,	  D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD,   0 },
		{ 1,   0, D3DDECLTYPE_FLOAT4,		D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION,		1 },
		D3DDECL_END ()
	};

	if (FAILED(d3dDevice->CreateVertexDeclaration (elements, &d3dvd))) return false;
	if (FAILED(d3dDevice->CreateVertexBuffer (sizeof (Vertex) * numVertices, 0, VERTEX_FVF, D3DPOOL_DEFAULT, &d3dvb, 0))) return false;
	if (FAILED(d3dDevice->CreateIndexBuffer (sizeof (unsigned short) * numIndices, 0, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &d3dib, 0))) return false;

	if (SUCCEEDED(d3dvb->Lock (0, sizeof (Vertex) * numVertices, &data, 0)))
	{
		memcpy (data, vertices, sizeof (Vertex) * numVertices);
		d3dvb->Unlock();
	}
	if (SUCCEEDED(d3dib->Lock (0, sizeof (unsigned short) * numIndices, &data, 0)))
	{
		memcpy (data, indices, sizeof (unsigned short) * numIndices);
		d3dib->Unlock ();
	}

	ceInstancedMesh* imesh = new ceInstancedMesh ();
	imesh->SetVertices(d3dvb, numIndices);
	imesh->SetIndices (d3dib, numIndices / 3);
	imesh->SetVertexDeclaration (d3dvd, sizeof (Vertex));
	imesh->SetInstanceRadius (radius);

  return imesh;
}

ceRenderMesh* CreateSphere (float radius, unsigned details)
{
	if (details < 3)
    {
      details = 3;
    }

  unsigned numVerts = details * details * 2;

	Vertex* verts = new Vertex [numVerts];
  unsigned p = 0;
  for (unsigned i = 0; i < details; i++)
    {
      float angI = (float) i * (float) D3DX_PI / (float) (details - 1);
      float tu = (float) i / (float) (details - 1);
      for (unsigned j = 0; j < details * 2; j++)
        {
          float angJ = (float) j * (float) D3DX_PI * 2.0f / (float) (details * 2 - 1);
          float tv = (float) j / (float) (details * 2 - 1);

					D3DXVECTOR3 v;
          v.x = cosf (angJ) * radius * sin (angI);
          v.y = cosf (angI) * radius;
          v.z = sinf (angJ) * radius * sin (angI);
					D3DXVECTOR3 n;
					D3DXVec3Normalize (&n, &v);

					verts[p].x = v.x;
					verts[p].y = v.y;
					verts[p].z = v.z;
					verts[p].nx = n.x;
					verts[p].ny = n.y;
					verts[p].nz = n.z;
					verts[p].tu = tu;
					verts[p].tv = tv;
          p++;
        }
    }

  unsigned numIndices = (details - 1) * (details * 2 - 1) * 6;
  unsigned short* indices = new unsigned short[numIndices];
  p = 0;
  for (unsigned i = 0; i < details - 1; i++)
    {
      unsigned i0 = i * details * 2;
      unsigned i1 = (i + 1) * details * 2;
      for (unsigned j = 0; j < details * 2 - 1; j++)
        {
          unsigned i00 = i0 + j;
          unsigned i01 = i0 + j + 1;
          unsigned i10 = i1 + j;
          unsigned i11 = i1 + j + 1;
          indices[p++] = i00;
          indices[p++] = i01;
          indices[p++] = i11;

          indices[p++] = i00;
          indices[p++] = i11;
          indices[p++] = i10;
        }
    }

  unsigned numberOfTrigs = numIndices / 3;
	printf ("NumberOfTrigs: %d\n", numberOfTrigs);
	LPDIRECT3DVERTEXDECLARATION9 d3dvd;
	LPDIRECT3DVERTEXBUFFER9 d3dvb;
	LPDIRECT3DINDEXBUFFER9 d3dib;
	void* data;

	D3DVERTEXELEMENT9 elements [] = {
		{ 0,   0, D3DDECLTYPE_FLOAT3,		D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION,		0 },
		{ 0,  12, D3DDECLTYPE_FLOAT3,	  D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL,			0 },
		{ 0,  24, D3DDECLTYPE_FLOAT2,	  D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD,   0 },
		D3DDECL_END ()
	};

	if (FAILED(d3dDevice->CreateVertexDeclaration (elements, &d3dvd))) return false;
	if (FAILED(d3dDevice->CreateVertexBuffer (sizeof (Vertex) * numVerts, 0, VERTEX_FVF, D3DPOOL_DEFAULT, &d3dvb, 0))) return false;
	if (FAILED(d3dDevice->CreateIndexBuffer (sizeof (unsigned short) * numIndices, 0, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &d3dib, 0))) return false;

	if (SUCCEEDED(d3dvb->Lock (0, sizeof (Vertex) * numVerts, &data, 0)))
	{
		memcpy (data, verts, sizeof (Vertex) * numVerts);
		d3dvb->Unlock();
	}
	if (SUCCEEDED(d3dib->Lock (0, sizeof (unsigned short) * numIndices, &data, 0)))
	{
		memcpy (data, indices, sizeof (unsigned short) * numIndices);
		d3dib->Unlock ();
	}

	ceRenderMesh* rmesh = new ceRenderMesh ();
	rmesh->SetVertices(d3dvb, numIndices);
	rmesh->SetIndices (d3dib, numberOfTrigs);
	rmesh->SetVertexDeclaration (d3dvd, sizeof (Vertex));
	rmesh->GetBoundingBox ().Clear ();
	for (unsigned i=0; i<numVerts; i++)
	{
		rmesh->GetBoundingBox ().AddVector3 (verts[i].x, verts[i].y, verts[i].z);
	}
	rmesh->GetBoundingBox ().Update ();

  return rmesh;
}

#define QUAD(i)		(i*4+0), (i*4+1), (i*4+2),   (i*4+0), (i*4+2), (i*4+3)

ceRenderMesh* CreateBox (float x, float y, float z)
{
	struct Vertex 
	{
		float x, y, z;
		float nx, ny, nz;
		float tu, tv;
	};
	D3DXVECTOR3 v (x, y, z);
	D3DXVec3Normalize (&v, &v);
	float nx = v.x;
	float ny = v.y;
	float nz = v.z;

	Vertex verts [] = {
		{ -x, -y,  -z,    -nx, -ny, -nz,    0, 0, },
		{ -x,  y,  -z,    -nx,  ny, -nz,    0, 1, },
		{  x,  y,  -z,     nx,  ny, -nz,    1, 1, },
		{  x, -y,  -z,     nx, -ny, -nz,    1, 0, },

		{  x, -y,  -z,     nx, -ny, -nz,    0, 0, },
		{  x,  y,  -z,     nx,  ny, -nz,    0, 1, },
		{  x,  y,   z,     nx,  ny,  nz,    1, 1, },
		{  x, -y,   z,     nx, -ny,  nz,    1, 0, },

		{  x, -y,   z,     nx, -ny,  nz,    0, 0, },
		{  x,  y,   z,     nx,  ny,  nz,    0, 1, },
		{ -x,  y,   z,    -nx,  ny,  nz,    1, 1, },
		{ -x, -y,   z,    -nx, -ny,  nz,    1, 0, },

		{ -x, -y,   z,    -nx, -ny,  nz,    0, 0, },
		{ -x,  y,   z,    -nx,  ny,  nz,    0, 1, },
		{ -x,  y,  -z,    -nx,  ny, -nz,    1, 1, },
		{ -x, -y,  -z,    -nx, -ny, -nz,    1, 0, },

		{ -x,  y,  -z,    -nx,  ny, -nz,    0, 0, },
		{ -x,  y,   z,    -nx,  ny,  nz,    0, 1, },
		{  x,  y,   z,     nx,  ny,  nz,    1, 1, },
		{  x,  y,  -z,     nx,  ny, -nz,    1, 0, },

		{ -x, -y,   z,    -nx, -ny,  nz,    0, 0, },
		{ -x, -y,  -z,    -nx, -ny, -nz,    0, 1, },
		{  x, -y,  -z,     nx, -ny, -nz,    1, 1, },
		{  x, -y,   z,     nx, -ny,  nz,    1, 0, },
	};

	unsigned short indices [] = {
		QUAD(0),
		QUAD(1),
		QUAD(2),
		QUAD(3),
		QUAD(4),
		QUAD(5),
	};

	unsigned numVerts = sizeof (verts) / sizeof (Vertex);
	unsigned numIndices = sizeof (indices) / sizeof (unsigned short);
	unsigned numTrigs = numIndices / 3;

	LPDIRECT3DVERTEXDECLARATION9 d3dvd;
	LPDIRECT3DVERTEXBUFFER9 d3dvb;
	LPDIRECT3DINDEXBUFFER9 d3dib;
	void* data;

	D3DVERTEXELEMENT9 elements [] = {
		{ 0,   0, D3DDECLTYPE_FLOAT3,		D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION,		0 },
		{ 0,  12, D3DDECLTYPE_FLOAT3,	  D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL,			0 },
		{ 0,  24, D3DDECLTYPE_FLOAT2,	  D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD,   0 },
		D3DDECL_END ()
	};

	if (FAILED(d3dDevice->CreateVertexDeclaration (elements, &d3dvd))) return false;
	if (FAILED(d3dDevice->CreateVertexBuffer (sizeof (Vertex) * numVerts, 0, VERTEX_FVF, D3DPOOL_DEFAULT, &d3dvb, 0))) return false;
	if (FAILED(d3dDevice->CreateIndexBuffer (sizeof (unsigned short) * numIndices, 0, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &d3dib, 0))) return false;

	if (SUCCEEDED(d3dvb->Lock (0, sizeof (Vertex) * numVerts, &data, 0)))
	{
		memcpy (data, verts, sizeof (Vertex) * numVerts);
		d3dvb->Unlock();
	}
	if (SUCCEEDED(d3dib->Lock (0, sizeof (unsigned short) * numIndices, &data, 0)))
	{
		memcpy (data, indices, sizeof (unsigned short) * numIndices);
		d3dib->Unlock ();
	}

	ceRenderMesh* rmesh = new ceRenderMesh ();
	rmesh->SetVertices(d3dvb, numIndices);
	rmesh->SetIndices (d3dib, numTrigs);
	rmesh->SetVertexDeclaration (d3dvd, sizeof (Vertex));
	rmesh->GetBoundingBox ().Clear ();
	for (unsigned i=0; i<numVerts; i++)
	{
		rmesh->GetBoundingBox ().AddVector3 (verts[i].x, verts[i].y, verts[i].z);
	}
	rmesh->GetBoundingBox ().Update ();

  return rmesh;

}


float GetRandom (float randomness)
{
	int r = rand();
	float v = (float)r / (float)RAND_MAX;
	return v * randomness;
}

ceTerrainMesh* CreateTerrain (TerrainSize ts, TerrainPatchSize tps, float size, float height, float randomness)
{
	srand(0);

	float *heights = new float[ts*ts];
	ZeroMemory(heights, ts*ts*sizeof(float));
	for (unsigned y=0; y<ts; y++)
	{
		float fy = (float)y * D3DX_PI / (float)(ts-1);
		fy *= 3.0f;
		for (unsigned x=0; x<ts; x++)
		{
			float fx = (float)x * D3DX_PI / (float)(ts-1);
			fx *= 5.0f;

			heights [y * ts + x] = sin (fx) * cos (fy) * height + GetRandom (randomness);

		}
	}

	ceTerrainMesh *mesh = new ceTerrainMesh ();
	if (mesh->Initialize (d3dDevice, heights, size, ts, tps))
	{
		return mesh;
	}

	mesh->Release ();
	return 0;
}

ceParticelMesh* CreateParticels (LPDIRECT3DDEVICE9 device, float dw, float dh, float w, float h, float d, unsigned numParts)
{
	ceParticelMesh* mesh = new ceParticelMesh ();
	if (!mesh->CreateParticel (device, dw, dh))
	{
		delete mesh;
		return 0;
	}

	for (unsigned i=0; i<numParts; i++)
	{
		D3DXVECTOR3 p (-w + GetRandom (2*w),
									 -h + GetRandom (2*h),
									 -d + GetRandom (2*d));
		mesh->AddParticel (p);
	}
	mesh->UpdateBoundingBox ();

	return mesh;
}



ceGeometryNode* AddGeometryNode (iGeometry* geometry, ceMaterial *material, D3DXVECTOR3 position, float min, float max, ceNodeCollection* collection)
{
	ceGeometryNode* node = new ceGeometryNode ();
	node->SetGeometry(geometry);
	node->SetMaterial (material);
	node->BeginTransformation ();
	D3DXMatrixTranslation (&node->GetTransform ().GetMatrix (), position.x, position.y, position.z);
	node->EndTransformation();
	node->SetRenderingRange (min, max);

	collection->AddNode (node);

	return node;
}




bool InitData (iViewport* viewport)
{
	physics = new cePhysics ();
	physics->Initialize (D3DXVECTOR3(0, -98.1f, 0.0f));

	camera = new ce3rdPersonCamera (viewport);
	camera->SetAt (D3DXVECTOR3(0, 50, 0));
	camera->GetProjector ()->SetFar (2048 * 2);
	camera->GetProjector ()->Update (45.0f);
	camera->SetDistance (100.0f);

	controller->SetRenderSort (CERS_SortMaterial);

	matManager->RegisterEffect ("ppTest0", "..\\media\\shaders\\pptest0.fx");
	ppTest0 = matManager->GetEffect ("ppTest0");
	printf ("TEstpp0: %p\n", ppTest0);

	ceMaterial* material = matManager->GetMaterial ("Simple");
	ceMaterial* terrainMaterial = matManager->GetMaterial ("Terrain");
	ceMaterial* grassMaterial = matManager->GetMaterial ("Grass");
	ceMaterial* dustMaterial = matManager->GetMaterial ("DustParticel");
	ceMaterial* dustMaterial2 = matManager->GetMaterial ("DustParticel2");
	ceMaterial* blockDirt = matManager->GetMaterial ("BlockDirt");
	ceMaterial* skinMat = matManager->GetMaterial ("SimpleSkin");


	ceNodeCollection* collection = new ceNodeCollection();

	ZeroMemory(&light, sizeof (D3DLIGHT9));
	light.Ambient = D3DXCOLOR(0, 0, 0, 0);
	light.Diffuse = D3DXCOLOR(1, 1, 1, 1);
	light.Specular = D3DXCOLOR(1, 1, 1, 1);
	light.Position = D3DXVECTOR3(100, 100, 100);
	
	controller->SetLight (light);

	ceSector* sector = new ceSector ();
	sector->AddNode (collection);


	scene = new ceScene ();
	scene->AddSector (sector);

	ceTerrainMesh* tmesh = CreateTerrain(TS_513, TPS_65, 1000.0f, 0.0f, 1.0f);
	ceNode* node = AddGeometryNode (tmesh, terrainMaterial, D3DXVECTOR3(0, 0, 0), 0.0f, 10000.0f, collection);
	ceRange r = node->GetRenderingRange ();

	btStaticPlaneShape* csPlaneShape = new btStaticPlaneShape (btVector3 (0, 1, 0), 0.0f);
	physics->AddStatic (csPlaneShape);

	ceRenderMesh* sphere10 = CreateSphere (10.0f, 3);
	ceRenderMesh* sphere20 = CreateSphere (20.0f, 32);
	ceRenderMesh* sphere40 = CreateSphere (40.0f, 32);
	btSphereShape* csSphere = new btSphereShape (10.0f);

	ceRenderMesh* box = CreateBox (30.0f, 10.0f, 10.0f);


	for (unsigned i=0; i<5; i++)
	{
		BlockNodeAnimator* bna=  new BlockNodeAnimator (D3DXVECTOR3(0, -100, 0), D3DXVECTOR3(0, -100, 0), 0, 0);
		ceNode* boxNode0 = AddGeometryNode (box, blockDirt, D3DXVECTOR3(0, 0, 0), 0.0f, 100000.0f, collection);
		boxNode0->SetNodeAnimator (bna);
		blockAnimators.push_back (bna);
	}

#if 1
	ceInstancedMesh* grass = CreateGrass (25.0f, 15.0f);


	for (unsigned i=0; i<100; i++)
	{
		float fi = i / 99.0f;
		for (unsigned j=0; j<100; j++)
		{
			float fj = j / 99.0f;

			float px = fi + RANDOM(0.02) - 0.01f;
			float pz = fj + RANDOM(0.02) - 0.01f;

			px = px < 0 ? 0.0f : (px > 1.0 ? 1.0 : px);
			pz = pz < 0 ? 0.0f : (pz > 1.0 ? 1.0 : pz);

			D3DXVECTOR3 pos = tmesh->GetPosition (px, pz);
			grass->AddInstance (pos);
		}
	}
	grass->Pack (d3dDevice);
	grass->UpdateBoundingBox();
	grass->GetBoundingBox().Debug ("Grass BoundingBox");

	ceNode* grassNode = AddGeometryNode (grass, grassMaterial, D3DXVECTOR3(0, 0, 0), 0, 10000.0f, collection);
#endif

#if 1
	float size = 200.0f, height = 1000.0f;
	blockParticels = new ceParticelNode ();
	ceParticelNode::Configuration config;
	ZeroMemory (&config, sizeof (config));
	config.ParticelDimension = D3DXVECTOR2(60.0f, 60.0f);
	config.Direction = D3DXVECTOR3(0, 1.0f, 0);
	config.DirectionVariance = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
	config.ParticelsPerSecond = 20;
	config.MaxParticels = 0;
	config.Gravity = D3DXVECTOR3(0, 0.0, 0);
	config.Pos = D3DXVECTOR3(0, 20, 0);
	config.PosVariance = D3DXVECTOR3(50.0f, 40.0f, 20.0f);
	config.TimeToLive = 2000;
	config.TimeToLiveVariance = 0;
	config.AlphaBlend = 0.9f;
	config.Material = dustMaterial;
	config.BoundingBoxUpdateInterval = 100;
	config.Rotation = 0.0f;
	config.RotationVariance = 0.001f;
	if (blockParticels->Configure (d3dDevice, config))
	{
		blockParticels->BeginTransformation ();
		D3DXMatrixTranslation (&blockParticels->GetTransform ().GetMatrix (), 200, 0, 200);
		blockParticels->EndTransformation();
		collection->AddNode (blockParticels);
		blockParticels->SetEmitting (false);
	}

	blockParticels2 = new ceParticelNode ();
	ZeroMemory (&config, sizeof (config));
	config.ParticelDimension = D3DXVECTOR2(20.0f, 20.0f);
	config.Direction = D3DXVECTOR3(0, 240.0f, 0);
	config.DirectionVariance = D3DXVECTOR3(100.0f, 0.0f, 100.0f);
	config.ParticelsPerSecond = 60;
	config.MaxParticels = 0;
	config.Gravity = D3DXVECTOR3(0, 0.0, 0);
	config.Pos = D3DXVECTOR3(0, 0, 0);
	config.PosVariance = D3DXVECTOR3(30.0f, 30.0f, 20.0f);
	config.TimeToLive = 250;
	config.TimeToLiveVariance = 0;
	config.AlphaBlend = 0.9f;
	config.Material = dustMaterial2;
	config.BoundingBoxUpdateInterval = 100;
	config.Rotation = 0.0f;
	config.RotationVariance = 0.1f;
	if (blockParticels2->Configure (d3dDevice, config))
	{
		blockParticels2->BeginTransformation ();
		D3DXMatrixTranslation (&blockParticels2->GetTransform ().GetMatrix (), 200, 0, 200);
		blockParticels2->EndTransformation();
		collection->AddNode (blockParticels2);
		blockParticels2->SetEmitting (false);
	}

#endif



	iGeometry* boneTubes = CreateBoneMesh (16, 20.0f, 40.0f, 4);
	ceGeometryNode* boneNode = AddGeometryNode (boneTubes, skinMat, D3DXVECTOR3(0, 40.0f, 0), 0.0f, 10000000.0f, collection);

	skeleton = new ceSkeleton ();
	ceBone* bone0 = skeleton->CreateBone ("Bone0", 40.0f);
	ceBone* bone1 = skeleton->CreateBone ("Bone1", 40.0f, bone0);
	ceBone* bone2 = skeleton->CreateBone ("Bone1", 40.0f, bone1);
	ceBone* bone3 = skeleton->CreateBone ("Bone1", 40.0f, bone2);
	skeleton->Build ();
	skeleton->Update ();
	skeleton->Debug ();
	boneNode->SetGeometryAnimator (skeleton);

	debug_node_collection = true;
	sector->BeginTransformation ();
	sector->EndTransformation();
	sector->GetBoundingBox ().      Debug("sector      ");

	return true;
}

void SetupMatrices()
{
	static float angle = 0.0f, langle = 0.0f;
	/*
	camera->SetAngleH (-0.4f);
	camera->SetAngleV (angle);
	camera->SetDistance (200.0f);
	camera->Update();
	*/
	// D3DXVECTOR3 f (200 * cos (angle), 100, 200 * sin(angle));
	/// camera->SetFrom (f);

	controller->SetCamera (camera);

	light.Position = D3DXVECTOR3(800.0f * cos (langle * 10.0f), 1000.0f, 800.0f * sin (langle * 10.0f));
	controller->SetLight (light);

	langle += 0.001f;
	angle += 0.001f;
}


void Update ()
{
	static long time = ceVClock::GetMilliseconds();

	ceUpdateEnv env;
	env.CurrentTime = ceVClock::GetMilliseconds();
	env.DeltaTime = env.CurrentTime - time;

	if (env.DeltaTime == 0)
	{
		return;
	}

	//dustEmitter->Update (env);

	time = env.CurrentTime;

	static float bone_angle = 0.0f;
	ceBone* bone = 0;
	for (unsigned i=0; i<skeleton->GetNumberOfBones (); i++)
	{
		bone = skeleton->GetBone (i);
		bone->SetRotation (D3DXVECTOR3 (1, 0, 0), bone_angle);
	}

	bone_angle -= 0.005f;
	skeleton->Update();
}

void Render (HWND hWnd)
{
	// prepare the viewing frustum
	scene->Collect (camera, true, controller);
	//controller->DebugContent ();
	LPDIRECT3DSURFACE9 surf, orig;
	d3dDevice->GetRenderTarget (0, &orig);
	pp0Texture->GetSurfaceLevel (0, &surf);
	d3dDevice->SetRenderTarget (0, surf);

	SetupMatrices ();
	d3dDevice->Clear (0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,0), 1.0f, 0);
	d3dDevice->BeginScene ();


	scene->Render (controller, viewport);
	d3dDevice->SetRenderTarget (0, orig);

	cePostProcessRenderer ppRenderer (d3dDevice);
	ppRenderer.Init2D (viewport);
	ppRenderer.RenderFSQuad (pp0Texture, ppTest0, pp0Effect);
	d3dDevice->EndScene();
	d3dDevice->Present (0, 0, 0, 0);
}

void Cleanup ()
{
	if (controller)
	{
		controller->Release ();
		controller = 0;
	}

	if (mouse)
	{
		mouse->Release();
		mouse = 0;
	}

	if (keyboard)
	{
		keyboard->Release ();
		keyboard = 0;
	}

	if (lpInput)
	{
		lpInput->Release ();
		lpInput = 0;
	}

	if (d3dDevice)
	{
		d3dDevice->Release();
		d3dDevice = 0;
	}
	if (d3d)
	{
		d3d->Release ();
		d3d = 0;
	}

}

bool CreateInputDevice (HWND hWnd, HINSTANCE hInst)
{
	if (FAILED(DirectInput8Create(hInst, DIRECTINPUT_VERSION, IID_IDirectInput8, (LPVOID*)&lpInput, 0))) return false;

	mouse = new ceMouse();
	mouse->AddRef ();
	if (!mouse->Initialize (lpInput, hWnd))
	{
		mouse->Release ();
		mouse = 0;
		return false;
	}
	keyboard = new ceKeyboard ();
	keyboard->AddRef ();
	if (!keyboard->Initialize (lpInput))
	{
		mouse->Release ();
		keyboard->Release ();
		mouse = 0;
		keyboard = 0;
		return false;
	}
	
	return true;
}

bool GetDisplayMode (int width, int height, D3DDISPLAYMODE* mode)
{
	D3DFORMAT format = D3DFMT_X8R8G8B8;
	UINT count = d3d->GetAdapterModeCount(D3DADAPTER_DEFAULT, format);
	for (UINT i=0; i<count; i++)
	{
		d3d->EnumAdapterModes (D3DADAPTER_DEFAULT, format, i, mode);
		if (mode->Width == width && mode->Height == height)
		{
			return true;
		}
	}
	return false;
}

bool Create3DDevice (HWND hWnd)
{
	d3d = Direct3DCreate9(D3D_SDK_VERSION);
	if (!d3d)
	{
		return false;
	}

	D3DDISPLAYMODE mode;
	if (!GetDisplayMode(WIDTH, HEIGHT, &mode))
	{
		printf ("no mode\n");
		return false;
	}


	D3DPRESENT_PARAMETERS d3dpp;
	ZeroMemory(&d3dpp, sizeof (d3dpp));
	d3dpp.BackBufferWidth = WIDTH;
	d3dpp.BackBufferHeight = HEIGHT;
	d3dpp.BackBufferFormat = mode.Format;
	d3dpp.BackBufferCount = 1;
	d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
	d3dpp.MultiSampleQuality = 0;
	d3dpp.SwapEffect = D3DSWAPEFFECT_FLIP;
	d3dpp.hDeviceWindow = hWnd;
	d3dpp.Windowed = TRUE;
	d3dpp.EnableAutoDepthStencil = TRUE;
	d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
	d3dpp.Flags = 0;
	d3dpp.FullScreen_RefreshRateInHz = d3dpp.Windowed ? 0 : mode.RefreshRate;
	d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;

	if (FAILED(d3d->CreateDevice (D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &d3dDevice)))
	{
		return false;
	}

	d3dDevice->SetRenderState (D3DRS_LIGHTING, false);

	controller = new ceRenderController ();
	controller->SetDevice (d3dDevice);

	matManager = ceMaterialManager::Initialize (d3dDevice);
	matManager->AddTextureLoader (new ceD3D9TextureLoader ());
	matManager->AddEffectLoader (new ceEffectLoader());
	matManager->AddMaterialLoader (new ceXMLMaterialLoader ());

	matManager->RegisterMaterial ("../media/materials/material.xml");
	matManager->RegisterMaterial ("../media/materials/skin.xml");
	matManager->RegisterMaterial ("../media/materials/terrain.xml");
	matManager->RegisterMaterial ("../media/materials/grass.xml");
	matManager->RegisterMaterial ("../media/materials/dust_particel.xml");
	matManager->RegisterMaterial ("../media/materials/dust_particel2.xml");
	matManager->RegisterMaterial ("../media/materials/block_dirt.xml");

	d3dDevice->CreateTexture (WIDTH, HEIGHT, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &pp0Texture, 0);

	D3DXCreateTextureFromFileA (d3dDevice, "../media/textures/noise.jpg", &pp0Effect);

	return true;
}


void PollInput ()
{
	if (mouse && keyboard && mouse->UpdateState () && keyboard->UpdateState ())
	{

		if (keyboard->IsKeyReleased(DIK_ESCAPE))
		{
			running = false;
		}

	}
}

void HandleCameraInput ()
{
	D3DXVECTOR3 dir;
	ZeroMemory (&dir, sizeof (D3DXVECTOR3));
	dir.x += keyboard->IsKeyDown (DIK_A) ? 1 : 0;
	dir.x -= keyboard->IsKeyDown (DIK_D) ? 1 : 0;
	dir.z += keyboard->IsKeyDown (DIK_S) ? 1 : 0;
	dir.z -= keyboard->IsKeyDown (DIK_W) ? 1 : 0;

	if (keyboard->IsKeyDown (DIK_LSHIFT) || keyboard->IsKeyDown (DIK_RSHIFT))
	{
		dir *= 2.0f;
	}

	float h = -mouse->GetDY () * 0.01f;
	float v = mouse->GetDX () * 0.01f;

	camera->RotateH (h);
	camera->RotateV (v);
	camera->SetDistance (100.0f);

	camera->Move (dir);
	camera->Update ();


}

LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
	switch( msg )
	{
	case WM_DESTROY:
		Cleanup();
		PostQuitMessage( 0 );
		return 0;
	}

	return DefWindowProc( hWnd, msg, wParam, lParam );
}

void debug_matrix (const char* name, float* mat)
{
	printf ("%s\n", name);
	printf ("  %.2f %.2f %.2f %.2f\n", mat[0], mat[1], mat[2], mat[3]);
	printf ("  %.2f %.2f %.2f %.2f\n", mat[4], mat[5], mat[6], mat[7]);
	printf ("  %.2f %.2f %.2f %.2f\n", mat[8], mat[9], mat[10], mat[11]);
	printf ("  %.2f %.2f %.2f %.2f\n", mat[12], mat[13], mat[14], mat[15]);
}

void debug_d3dxmatrix (const D3DXMATRIX& mat)
{
	debug_matrix ("D3DXMATRIX", (float*)&mat);
}

int main1 (int argc, char** argv)
{
	WNDCLASSEX wc =
	{
		sizeof( WNDCLASSEX ), CS_CLASSDC, MsgProc, 0L, 0L,
		GetModuleHandle( NULL ), NULL, NULL, NULL, NULL,
		"CECLASS", NULL
	};
	RegisterClassEx( &wc );

	// Create the application's window
	HWND hWnd = CreateWindow( "CECLASS", "Crossengine v0.0.1",
		WS_OVERLAPPED | WS_THICKFRAME, 400, 10, WIDTH, HEIGHT,
		NULL, NULL, wc.hInstance, NULL );

	viewport = new MyViewport (WIDTH, HEIGHT);

	if (!CreateInputDevice (hWnd, wc.hInstance))
	{
		Cleanup ();
		return 0;
	}
	if (!Create3DDevice (hWnd))
	{
		Cleanup ();
		return 0;
	}
	if (!InitData (viewport))
	{
		Cleanup();
		return 0;
	}


	ShowWindow( hWnd, SW_SHOWDEFAULT );
	UpdateWindow( hWnd );

	// Enter the message loop
	MSG msg;
	ZeroMemory( &msg, sizeof( msg ) );
	ceTimer fpsTimer;
	unsigned lastFps = fpsTimer.GetFPS ();
	char text[256];
	ceVClock::Tick();
	long time = ceVClock::GetMilliseconds();
	while( msg.message != WM_QUIT && running)
	{
		ceVClock::Tick ();
		fpsTimer.Tick ();
		long cTime = ceVClock::GetMilliseconds();

		unsigned fps = fpsTimer.GetFPS ();
		if (fps != lastFps)
		{
			lastFps = fps;
			sprintf (text, "Crossengine v0.0.1 - FPS: %d", fps);
			SetWindowText (hWnd, text);
		}


		HandleBlock (keyboard->IsKeyDown (DIK_SPACE));
		/*
		if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
		{
			TranslateMessage( &msg );
			DispatchMessage( &msg );
		}
		*/
		{
			long tDiff = cTime - time;
			if (tDiff != 0)
			{
				physics->Step (tDiff / 1000.0f);
			}
			PollInput ();
			HandleCameraInput ();
			Update ();
			Render(hWnd);
		}
		time = cTime;
	}

	UnregisterClass( "CECLASS", wc.hInstance );
	return 0;
}



int main2(int argc, char** argv)
{
	ceString name = "hallo.text.txt";

	unsigned pos = name.rfind (".", -1);
	printf ("pos: %d\n", pos);
	ceString ext = name.substr (pos+1, name.length ());
	printf ("ext: %s\n", ext.c_str ());
	fgetchar ();

	return 0;
}
